﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Text;
using System.Text.RegularExpressions;
using System.Windows.Forms;

namespace PluginDependencyTest
{
    public class AssemblyAnalyzer
    {

        public static HashSet<string> windowsDlls = new HashSet<string>()
        {
            "mscorlib.dll",
            "PresentationCore.dll",
            "PresentationFramework.dll",
            "WindowsBase.dll",
            "wpfgfx_v0400.dll",
            "user32.dll",
            "gdi32.dll",
            "dwmapi.dll",
            "D3DCompiler_47.dll",
            "clr.dll",
            "penimc.dll",
        };

        private static void TryLoad(string path, StringBuilder sb)
        {
            try
            {
                Assembly handle = System.Reflection.Assembly.LoadFile(path);
                if (handle == null)
                {
                    sb.AppendLine(path + " 加载失败！");
                }
            }
            catch (Exception exception)
            {
                sb.AppendLine(path + " 加载失败！" + exception.Message);
            }
        }

        private static bool TryLoad(AssemblyName assemblyName)
        {
            try
            {
                Assembly handle = System.Reflection.Assembly.Load(assemblyName);
                if (handle != null)
                    return true;
            }
            catch (Exception exception)
            {
            }

            //($"{assemblyName.Name}.dll 加载失败！");
            return false;
        }

        private static void TestLoad(string dllName, StringBuilder sb)
        {
            string path1 = AppDomain.CurrentDomain.BaseDirectory;
            string path2 = Path.Combine(path1, "SSDP");
            string path3 = Path.Combine(path1, "SysTools");

            var exist1 = File.Exists(Path.Combine(path1, dllName));
            var exist2 = File.Exists(Path.Combine(path2, dllName));
            var exist3 = File.Exists(Path.Combine(path3, dllName));

            if (!exist1 && !exist2 && !exist3)
                sb.AppendLine($"{dllName}加载失败，未能在SSDP目录下找到");

            if (exist1) TryLoad(Path.Combine(path1, dllName), sb);
            if (exist2) TryLoad(Path.Combine(path2, dllName), sb);
            if (exist3) TryLoad(Path.Combine(path3, dllName), sb);
        }

        public static void Analysis(TextBox tb)
        {
            var dlls = ParseAssemblyNames(tb.Text);

            StringBuilder sb = new StringBuilder();
            sb.AppendLine("解析结果：");
            sb.AppendLine();
            foreach (var lib in dlls)
            {
                sb.AppendLine(lib.ToString());
            }
            sb.AppendLine();
            sb.Append(Analysis(dlls));

            tb.Text = sb.ToString();
        }

        public static string Analysis(List<AssemblyName> dlls)
        {
            StringBuilder sb = new StringBuilder();

            foreach (var assembly in dlls)
            {
                if (TryLoad(assembly)) //尝试直接按名称加载
                    continue;

                var lib = assembly.Name;

                if (lib.StartsWith("System.") ||
                    lib.StartsWith("Microsoft.") ||
                    lib.StartsWith("api-ms-win") ||
                    lib.StartsWith("vcruntime") ||
                    lib.StartsWith("msvc") ||
                    windowsDlls.Contains(lib)
                    )
                    continue;

                var libName = lib;
                if (!libName.EndsWith(".dll"))
                    libName += ".dll";

                try
                {
                    TestLoad(libName, sb);
                }
                catch (Exception exception)
                {
                    sb.AppendLine(libName + " 加载失败！" + exception.Message);
                }
            }

            if (0 == sb.Length)
                sb.AppendLine("\n无异常");

            return sb.ToString();
        }

        public static List<AssemblyName> ParseAssemblyNames(string input)
        {
            List<AssemblyName> assemblyNames = new List<AssemblyName>();

            // 正则表达式匹配程序集信息，包括简单的dll名称
            string pattern = @"(?<name>[a-zA-Z0-9._]+), Version=(?<version>\d+\.\d+\.\d+\.\d+), Culture=(?<culture>[^,]+), PublicKeyToken=(?<token>[a-f0-9]+)|(?<name2>[a-zA-Z0-9._-]+\.dll)";

            MatchCollection matches = Regex.Matches(input, pattern, RegexOptions.Multiline);

            foreach (Match match in matches)
            {
                try
                {
                    if (match.Groups["name"].Success)
                    {
                        // 提取第一种格式
                        string name = match.Groups["name"].Value;
                        string version = match.Groups["version"].Value;
                        string culture = match.Groups["culture"].Value;
                        string token = match.Groups["token"].Value;

                        if (name.Contains(" "))
                            continue;

                        try
                        {
                            assemblyNames.Add(new AssemblyName($"{name}, Version={version}, Culture={culture}, PublicKeyToken={token}"));
                        }
                        catch (Exception)
                        {
                            assemblyNames.Add(new AssemblyName(name));
                        }               
                    }
                    else if (match.Groups["name2"].Success)
                    {
                        // 提取第二种格式
                        string name = match.Groups["name2"].Value;
                        assemblyNames.Add(new AssemblyName(name));
                    }
                }
                catch (Exception)
                {
                }
            }

            return assemblyNames;
        }
    }
}